// 1.6. 运算符-杂项运算符
/**
 * 算符是一种告诉编译器执行特定的数学或逻辑操作的符号。C++ 内置了丰富的运算符，并提供了以下类型的运算符：
* 
* 算术运算符
* 关系运算符
* 逻辑运算符
* 位运算符
* 赋值运算符
* 杂项运算符
* 本章将逐一介绍算术运算符、关系运算符、逻辑运算符、位运算符、赋值运算符和其他运算符。
 *
 * 
 * 杂项运算符
 * 
 * 运算符 sizeof
 * 描述 sizeof 运算符返回变量的大小。例如，sizeof(a) 将返回 4，其中 a 是整数。
 * 
 * 
 * 运算符 Condition ? X : Y
 * 描述 条件运算符。如果 Condition 为真 ? 则值为 X : 否则值为 Y。
 * 
 * 
 * 运算符 ,
 * 描述  逗号运算符会顺序执行一系列运算。整个逗号表达式的值是以逗号分隔的列表中的最后一个表达式的值。
 * 
 * 
 * 运算符 .（点）和 ->（箭头）
 * 描述  成员运算符用于引用类、结构和共用体的成员。
 * 
 * 
 * 运算符 Cast
 * 描述  强制转换运算符把一种数据类型转换为另一种数据类型。例如，int(2.2000) 将返回 2。
 * 
 * 
 * 运算符 &
 * 描述  指针运算符 & 返回变量的地址。例如 &a; 将给出变量的实际地址。
 * 
 * 运算符 *
 * 描述  指针运算符 * 指向一个变量。例如，*var; 将指向变量 var。。
*/

#include <iostream>
#include <string.h>
using namespace std;
int main(void)
{
  /**
   * 1. sizeof
   * sizeof 是一个关键字，它是一个编译时运算符，用于判断变量或数据类型的字节大小。
   * sizeof 运算符可用于获取类、结构、共用体和其他用户自定义数据类型的大小。
   * 使用 sizeof 的语法如下：
   * sizeof (data type)
   * 其中，data type 是要计算大小的数据类型，包括类、结构、共用体和其他用户自定义数据类型
  */
  cout << "-----------sizeof---------------" << endl;
  cout << "Size of char : " << sizeof(char) << endl;
  cout << "Size of int : " << sizeof(int) << endl;
  cout << "Size of short int : " << sizeof(short int) << endl;
  cout << "Size of long int : " << sizeof(long int) << endl;
  cout << "Size of float : " << sizeof(float) << endl;
  cout << "Size of double : " << sizeof(double) << endl;
  cout << "Size of wchar_t : " << sizeof(wchar_t) << endl;

  int num1 = 4;
  cout << "Size of int num : " << sizeof(num1) << endl; // 4 变量标识符也是可以的

  /**
   * 2.三元运算符
   * Exp1 ? Exp2 : Exp3;
   * ? 被称为三元运算符，因为它需要三个操作数，可以用来代替 if-else 语句
   * 
  */
  cout << "-----------三目运算符---------------" << endl;
  int val = 0;
  val = (50 > 20) ? 50 : 20; // 50

  cout << "val的值:" << val << endl;

  /**
   * 3.逗号运算符
   * 使用逗号运算符是为了把几个表达式放在一起。整个逗号表达式的值为系列中最后一个表达式的值。从本质上讲，逗号的作用是将一系列运算按顺序执行。
   * 表达式1, 表达式2
   * 求解过程是：先求解表达式 1，再求解表达式 2。整个逗号表达式的值是表达式 2 的值。
   * 最右边的那个表达式的值将作为整个逗号表达式的值，其他表达式的值会被丢弃。
   * 例如：
   * var = (count=19, incr=10, count+1);
   * 在这里，首先把 count 赋值为 19，把 incr 赋值为 10，然后把 count 加 1，最后，把最右边表达式 count+1 的计算结果 20 赋给 var。上面表达式中的括号是必需的，因为逗号运算符的优先级低于赋值操作符。
  */
  cout << "-----------逗号运算符---------------" << endl;
  val = 0;
  int count, incr;
  val = (count = 19, incr = 10, count + 1); // 20
  cout << "val 的值:" << val << " incr: " << incr << " count: " << count << endl;

  val = (count = 3, incr = 4, count + incr); // 7
  cout << "val 的值:" << val << " incr: " << incr << " count: " << count << endl;

  int i, j;
  j = 10;
  i = (j++, j + 100, 999 + j);
  cout << "i 的值:" << i       //1010
       << " j: " << j << endl; // 11

  j = 10;
  i = (j++, j += 100, 999 + j);
  cout << "i 的值:" << i       //1110
       << " j: " << j << endl; // 11

  /**
   * 4.成员运算符(点、箭头)
   * （点）运算符和 ->（箭头）运算符用于引用类、结构和共用体的成员。
   * 点运算符应用于实际的对象。
   * 箭头运算符与一个指向对象的指针一起使用。
  */
  cout << "-----------成员运算符(点、箭头)---------------" << endl;
  struct Employee
  {
    char name[16];
    int age;
  } emp;
  // 需引入string.h文件使用
  strcpy(emp.name, "XueLing"); // 把值 "zara" 赋给对象 emp 的 first_name 成员：
  emp.age = 18;

  cout << "emp.name:" << emp.name << endl;
  cout << "emp.age:" << emp.age << endl;

  Employee *pt;  //定义pt 为指向Employee类对像的指针变量
  Employee emp2; //定义emp2为Time类对像
  pt = &emp2;    //将emp2的起始地址赋给pt

  strcpy(pt->name, "XUELING"); // 把值 "XUELING" 赋给对象 emp 的 name 成员
  cout << "emp2.name:" << emp2.name << endl;

  // 简而言之，访问结构的成员时使用点运算符，而通过指针访问结构的成员时，则使用箭头运算符。

  /**
   * 5.强制转换运算符
   * 强制转换运算符是一种特殊的运算符，它把一种数据类型转换为另一种数据类型。强制转换运算符是一元运算符，它的优先级与其他一元运算符相同。
   * 大多数的 C++ 编译器都支持大部分通用的强制转换运算符：
   * (type) expression 
   * 其中，type 是转换后的数据类型。下面列出了 C++ 支持的其他几种强制转换运算符：
   * const_cast<type> (expr): const_cast 运算符用于修改类型的 const / volatile 属性。除了 const 或 volatile 属性之外，目标类型必须与源类型相同。这种类型的转换主要是用来操作所传对象的 const 属性，可以加上 const 属性，也可以去掉 const 属性。
   * 
   * dynamic_cast<type> (expr): dynamic_cast 在运行时执行转换，验证转换的有效性。如果转换未执行，则转换失败，表达式 expr 被判定为 null。dynamic_cast 执行动态转换时，type 必须是类的指针、类的引用或者 void*，如果 type 是类指针类型，那么 expr 也必须是一个指针，如果 type 是一个引用，那么 expr 也必须是一个引用。
   * 
   * reinterpret_cast<type> (expr): reinterpret_cast 运算符把某种指针改为其他类型的指针。它可以把一个指针转换为一个整数，也可以把一个整数转换为一个指针。
   * 
   * static_cast<type> (expr): static_cast 运算符执行非动态转换，没有运行时类检查来保证转换的安全性。例如，它可以用来把一个基类指针转换为派生类指针。
   * 
   * 上述所有的强制转换运算符在使用类和对象时会用到。现在，请看下面的实例，理解 C++ 中如何使用一个简单的强制转换运算符。
  */
  cout << "-----------强制转换运算符---------------" << endl;

  double d1 = 10.0001;
  float f1 = 0.0001;

  cout << "(int)d : " << (int)d1 << endl; // 10
  cout << "(int)f : " << (int)f1 << endl; // 0

  /**
   * 指针运算符(& *)
   * C++ 提供了两种指针运算符，一种是取地址运算符 &，一种是间接寻址运算符 *。
   * 指针是一个包含了另一个变量地址的变量，您可以把一个包含了另一个变量地址的变量说成是"指向"另一个变量。变量可以是任意的数据类型，包括对象、结构或者指针。
   * 
   * 取地址运算符 &
   * & 是一元运算符，返回操作数的内存地址。例如，如果 var 是一个整型变量，则 &var 是它的地址。该运算符与其他一元运算符具有相同的优先级，在运算时它是从右向左顺序进行的。
   * 您可以把 & 运算符读作"取地址运算符"，这意味着，&var 读作"var 的地址"。
   * 
   * 间接寻址运算符 *
   * 第二个运算符是间接寻址运算符 *，它是 & 运算符的补充。* 是一元运算符，返回操作数所指定地址的变量的值。
  */

  cout << "-----------指针运算符(& *)---------------" << endl;
  int var;
  int *ptr;
  int v;

  var = 300;
  ptr = &var; // 获取var的地址
  v = *ptr;   // 获取prt所指向的地址的变量的值

  cout << "var : " << var << endl;
  cout << "ptr : " << ptr << endl;
  cout << "v : " << v << endl;

  printf("----------end-----------\n");
  return 0;
}